﻿using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Inovout.Data.Domain;
using Inovout.Data.Repository;
using Inovout.Descriptor;
using Inovout.Web.Mvc;

namespace System.Web.Mvc.Html
{
    public class QueryCriteriaModel : QueryCriteria
    {
        public QueryCriteriaModel(Type modelDeclaringType, ViewDataDictionary viewData)
            : this(modelDeclaringType, (Type)viewData["ModelType"] ?? modelDeclaringType)
        {
        }
        public QueryCriteriaModel(Type modelDeclaringType, Type modelType)
            : base(modelType)
        {
            FilterCollection = new FilterCriteriaCollectionModel(modelDeclaringType, modelType);
        }
        public FilterCriteriaCollectionModel FilterCollection { get; private set; }
        public override List<FilterCriteria> Filters
        {
            get
            {
                return this.FilterCollection;
            }
            set
            {
                throw new NotSupportedException();
            }
        }
        public WidgetModelMetadata QueryModelMetadata
        {
            get { return this.FilterCollection.QueryModelMetadata; }
        }
        internal void Merge(IEnumerable<FilterCriteria> filters, IEnumerable<OrderCriteria> orders)
        {

            this.FilterCollection.Merge(filters);
            if (this.Orders.Count == 0 && orders != null)
            {
                this.Orders.AddRange(orders);

            }

        }
        public virtual int FilterIndex(FilterCriteria filter)
        {
            return this.FilterCollection.IndexOf(filter);
        }

        public virtual int FilterIndexAndSetValue(FilterCriteria filter)
        {
            return FilterCollection.FilterIndexAndSetValue(filter);
        }
        public IEnumerable<IGrouping<string, FilterCriteria>> GroupFilters
        {
            get
            {
                return this.FilterCollection.GroupFilters;
            }
        }
    }

    public class FilterCriteriaCollectionModel : List<FilterCriteria>
    {
        public FilterCriteriaCollectionModel(Type modelDeclaringType, ViewDataDictionary viewData)
            : this(modelDeclaringType, (Type)viewData["ModelType"] ?? modelDeclaringType)
        {
        }
        public FilterCriteriaCollectionModel(Type modelDeclaringType, Type modelType)
        {
            //因为集团为空，只能获取声明类型，而通常情况下声明类型不会有相应的Attriubte.
            this.ModelDeclaringType = modelDeclaringType;
            this.ModelType = modelType;
            this.modelTypeDescriptor = TypeDescriptor.Get(modelType);
        }

        private readonly TypeDescriptor modelTypeDescriptor;
        public Type ModelDeclaringType { get; private set; }
        private WidgetModelMetadata metadata;
        public Type ModelType { get; private set; }
        public WidgetModelMetadata QueryModelMetadata
        {
            get
            {
                if (metadata == null)
                {
                    metadata = ModelMetadataProviders.Current.GetMetadataForType(() => null, ModelType).AsWidgetModelMetadata();
                    if (this.ModelDeclaringType.IsAssignableFrom(ModelType))
                    {
                        metadata.ModelDeclaringType = this.ModelDeclaringType;
                    }
                    else
                    {
                        metadata.ModelDeclaringType = ModelType;
                    }
                }
                return metadata;
            }
        }
        internal void Merge(IEnumerable<FilterCriteria> filters)
        {
            if (filters != null)
            {
                // base.ModelType = queryCriterions.ModelType;
            }
            if (this.Count == 0 && filters != null)
            {
                this.AddRange(filters);
            }
            var metadata = this.QueryModelMetadata;
            metadata.SelectModelDeclaringTypeProperties(this.GroupFilters.Select(g => g.Key).ToArray());
        }
        private string GetPropertyName(string filterName)
        {
            string propertyName = filterName;
            var propertyDescriptor = modelTypeDescriptor.GetProperty(propertyName);
            if (typeof(DataObject).IsAssignableFrom(propertyDescriptor.DeclaringType) && propertyName.EndsWith(".Id"))
            {
                propertyName = propertyName.TrimEnd(".Id".ToCharArray());
            }
            return propertyName;
        }
        public virtual int FilterIndex(FilterCriteria filter)
        {
            return base.IndexOf(filter);
        }
        private static readonly IDictionary<Type, Func<string, object>> converts = new Dictionary<Type, Func<string, object>>
        {
             {typeof(DateTime), (v)=>DateTime.Parse(v)},
             {typeof(int), (v)=>int.Parse(v)},
             {typeof(float), (v)=>float.Parse(v)},
             {typeof(double), (v)=>double.Parse(v)},
             {typeof(long), (v)=>long.Parse(v)},
             {typeof(bool), (v)=>bool.Parse(v)},
             {typeof(TimeSpan), (v)=>TimeSpan.Parse(v)},
             {typeof(decimal), (v)=>decimal.Parse(v)},
             {typeof(byte), (v)=>byte.Parse(v)},
             {typeof(string), (v)=>v}
        };

        public virtual int FilterIndexAndSetValue(FilterCriteria filter)
        {

            var metadata = GetFilterModelMetadata(filter.FilterName);
            string formatString = metadata.QueryFormat ?? metadata.EditFormatString;
            if (!string.IsNullOrEmpty(filter.FilterValue))
            {
                if (!String.IsNullOrEmpty(formatString))
                {
                    object value = filter.FilterValue;
                    if (converts.ContainsKey(metadata.ModelType))
                    {
                        value = converts[metadata.ModelType](filter.FilterValue);
                    }
                    metadata.Model = String.Format(CultureInfo.CurrentCulture, formatString, value);
                }
                else if (metadata.ModelType.IsEnum)
                {
                    metadata.Model = Enum.Parse(metadata.ModelType, filter.FilterValue);
                }
                else
                {
                    metadata.Model = filter.FilterValue;
                }
            }

            return base.IndexOf(filter);
        }
        public virtual WidgetModelMetadata GetFilterModelMetadata(string filterName)
        {
            return this.QueryModelMetadata.Properties.OfType<WidgetModelMetadata>()
                .Single(p => p.FullPropertyName.Equals(GetPropertyName(filterName)));
        }
        public IEnumerable<IGrouping<string, FilterCriteria>> GroupFilters
        {
            get
            {
                return this.GroupBy(f => GetPropertyName(f.FilterName));
            }
        }
    }

}
